﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Xml.Serialization;
using Microsoft.Xna.Framework;
using Tomahawk.Runtime.Rendering.Objects;
using Tomahawk.Runtime.Scene;
using Tomahawk.Runtime.Rendering.Interfaces;
using Tomahawk.Runtime.Rendering.Materials;

using XMas;

namespace Tomahawk.Runtime.Logic
{
    public class XSkeletalMeshComponent : XMaterialMeshComponent
    {
        /// <summary>
        /// The XMas
        /// </summary>
        private XMAnimationSystem animationSystem;    

        /// <summary>
        /// Constructor
        /// </summary>
        public XSkeletalMeshComponent()
            : base()
        {
            animationSystem = null;
        }

        #region "Serializable properties"

        private string animGraphName;

        /// <summary>
        /// Indicates the name of the animation graph that will be used
        /// </summary>
        [XmlElement("AnimGraphName")]
        public string AnimGraphName
        {
            get { return animGraphName; }
            set { animGraphName = value; }
        }


        #endregion

        # region "Delegantes and events"

        /// <summary>
        /// Delegate used to anim end event
        /// </summary>
        /// <param name="NodeName">Node name that raise event</param>
        /// <param name="AnimName">Animation name</param>
        /// <param name="bLoop">If animation was a loop animation</param>
        public delegate void OnAnimEndDelegate(string NodeName, string AnimName, bool bLoop);
        /// <summary>
        /// Event raised when an animation that has bRaiseEndAnimEvent = true has finished
        /// </summary>
        public event OnAnimEndDelegate OnAnimEndEvent;

        #endregion

        # region "Cached events"

        /// <summary>
        /// Needed to pass skining data to material
        /// </summary>
        public void OnRenderMaterialPass(IRenderable source, SASMaterial material)
        {
            // Get the skin transforms and add
            if (animationSystem != null)
            {
                Matrix[] bones = animationSystem.GetSkinTransforms();
                material.Effect.Parameters["Bones"].SetMatrixArrayValue(ref bones);
            }
        }

        /// <summary>
        /// Send the event to our surcribe, with that nobody has to know about xmas
        /// </summary>
        private void OnAnimEnd(string NodeName, string AnimName, bool bLoop)
        {
            if (OnAnimEndEvent != null)
                OnAnimEndEvent(NodeName, AnimName, bLoop);
        }

        #endregion

        #region "Public Functions"

        public void SetReflexionObject(Object reflexionObject)
        {
            if (animationSystem != null)
                animationSystem.ReflexionObject = reflexionObject;
        }

        #endregion

        #region "Inhereit functions"

        /// <summary>
        /// Create XMAS object and load
        /// </summary>
        protected override void OnPreBeginPlay()
        {
            base.OnPreBeginPlay();

            // Create animation system and load graph
            CSkeletonData skinningData = MeshModel.Model.Tag as CSkeletonData;
            if (skinningData == null)
                throw new InvalidOperationException
                    ("This model does not contain a SkinningData tag.");

            animationSystem = XMAnimationSystem.LoadFromFile(animGraphName, skinningData, null);

            // Subscribe to animation end event
            animationSystem.OnAnimEndEvent += OnAnimEnd;
        }


        /// <summary>
        /// Update xmas system
        /// </summary>
        /// <param name="elapsedTime"></param>
        protected override void OnUpdate(float elapsedTime)
        {
            base.OnUpdate(elapsedTime);

            // Update xmas
            if (animationSystem != null)
                animationSystem.Update(TimeSpan.FromSeconds(elapsedTime));
        }


        /// <summary>
        /// Initialization
        /// </summary>
        protected override void OnInitialize()
        {
            base.OnInitialize();
            MeshModel.OnPrepareMaterial += new RenderMaterialDelegate(OnRenderMaterialPass);
        }

        #endregion

    }

}
